#pragma once

namespace gecko
{

	namespace gtl
	{

		template<typename T>
		EList<T>::EList()
		{
			m_Head = new T;
			m_Tail = new T;

			m_Head->Prev = 0;
			m_Head->Next = m_Tail;

			m_Tail->Prev = m_Head;
			m_Tail->Next = 0;

			m_Count = 0;
		}

		template<typename T>
		EList<T>::EList(const EList& list)
		{
			m_Head = new T;
			m_Tail = new T;

			m_Head->Prev = 0;
			m_Head->Next = m_Tail;

			m_Tail->Prev = m_Head;
			m_Tail->Next = 0;

			// copy over data from other list
			for ( ConstIterator itr = list.Begin(); itr != list.End(); ++itr )
			{
				PushBack(*itr);
			}
		}

		template<typename T>
		typename EList<T>::Iterator EList<T>::Begin()
		{
			return (Iterator(m_Head->Next));
		}

		template<typename T>
		typename EList<T>::Iterator EList<T>::End()
		{
			return (Iterator(m_Tail));
		}

		template<typename T>
		typename EList<T>::ConstIterator EList<T>::Begin() const
		{
			return (ConstIterator(m_Head->Next));
		}

		template<typename T>
		typename EList<T>::ConstIterator EList<T>::End() const
		{
			return (ConstIterator(m_Tail));
		}

		template<typename T>
		void EList<T>::Insert(T& data, Iterator before)
		{
			T* beforeNode = before.m_Current;
			T* nextNode = beforeNode;
			T* prevNode = beforeNode->Prev;

			data.Next = nextNode;
			data.Prev = prevNode;

			prevNode->Next = &data;
			nextNode->Prev = &data;

			++m_Count;
		}

		template<typename T>
		void EList<T>::Erase(Iterator where)
		{
			Node* toRemove = where.m_Current;
			Node* prevNode = toRemove->Prev;
			Node* nextNode = toRemove->Next;

			// unlink node
			prevNode->Next = nextNode;
			nextNode->Prev = prevNode;

			--m_Count;
		}

		template<typename T>
		void EList<T>::Remove(const T& data)
		{
			// make sure data is linked
			if (data.Next || data.Prev)
			{
#if defined(_DEBUG)
				// sanity check that this node is in this list
				Node* node = &data;
				bool isInList = false;
				while (node != m_Head)
				{
					node = node->Prev;
				}

				Assert(node == m_Head, "Item is not linked to this list");
#endif


				// unlink
				Node* prevNode = toRemove->Prev;
				Node* nextNode = toRemove->Next;

				// unlink node
				prevNode->Next = nextNode;
				nextNode->Prev = prevNode;

				--m_Count;
			}
		}

		template<typename T>
		typename EList<T>::Iterator EList<T>::Find(const T& data)
		{
			for ( Iterator itr = Begin(); itr != End(); ++itr )
			{
				if ( (*itr) == data )
				{
					return itr;
				}
			}

			return End();
		}

		template<typename T>
		typename EList<T>::ConstIterator EList<T>::Find(const T& data) const
		{
			for ( ConstIterator itr = Begin(); itr != End(); ++itr )
			{
				if ( (*itr) == data )
				{
					return itr;
				}
			}

			return End();
		}

		template<typename T>
		void EList<T>::PushBack(T& data)
		{
			Insert(data, End());
		}

		template<typename T>
		void EList<T>::PushFront(T& data)
		{
			Insert(data, Begin());
		}

		template<typename T>
		void EList<T>::PopFront()
		{
			Erase(Begin());
		}

		template<typename T>
		void EList<T>::PopBack()
		{
			Erase(--End());
		}

		template<typename T>
		T& EList<T>::Front()
		{
			return *m_Head->Next;
		}

		template<typename T>
		const T& EList<T>::Front() const
		{
			return *m_Head->Next;
		}

		template<typename T>
		T& EList<T>::Back()
		{
			return *m_Tail->Prev;
		}

		template<typename T>
		const T& EList<T>::Back() const
		{
			return *m_Tail->Prev;
		}

		template<typename T>
		void EList<T>::Clear()
		{
			T* front = m_Head->Next;

			for ( T* node = front->Next; node != 0 ; node = node->Next )
			{
				T* toRemove = node->Prev;
				toRemove->Prev = 0;
				toRemove->Next = 0;
			}

			m_Head->Next = m_Tail;
			m_Tail->Prev = m_Head;

			m_Count = 0;
		}

		template<typename T>
		unsigned int EList<T>::Size()
		{
			return m_Count;
		}

		template<typename T>
		bool EList<T>::Empty()
		{
			return m_Count == 0;
		}



		namespace internal
		{

			// base class for list iterators
			template<typename T>
			class EListIterator
			{
			public:
				// Default constructor.
				EListIterator();

				// Construct from node.
				EListIterator(T* node);

				// Copy constructor.
				EListIterator(const EListIterator& itr);

				// Pre-increment.
				EListIterator& operator++();

				// Post-increment.
				EListIterator& operator++(int);

				// Pre-decrement.
				EListIterator& operator--();

				// Post-decrement.
				EListIterator& operator--(int);

				// Equality operator.
				bool operator==(const EListIterator& itr) const;

				// Inequality operator.
				bool operator!=(const EListIterator& itr) const;

				// Dereference operator.
				T& operator*() const;

				// Dereference operator.
				T& operator*();

			private:
				// pointer to node
				T* m_Current;

				friend class EList<T>;
				friend class EList<const T>;
			};

			template<typename T>
			EListIterator<T>::EListIterator()
				: m_Current(0)
			{
			}

			template<typename T>
			EListIterator<T>::EListIterator(T* node)
				: m_Current(node)
			{
			}

			template<typename T>
			EListIterator<T>::EListIterator(const EListIterator& itr)
				: m_Current(itr.m_Current)
			{
			}

			template<typename T>
			EListIterator<T>& EListIterator<T>::operator++()
			{	
				m_Current = m_Current->Next;
				return (*this);
			}

			template<typename T>
			EListIterator<T>& EListIterator<T>::operator++(int)
			{	
				m_Current = m_Current->Next;
				return (*this);
			}

			template<typename T>
			EListIterator<T>& EListIterator<T>::operator--()
			{	
				m_Current = m_Current->Prev;
				return (*this);
			}

			template<typename T>
			EListIterator<T>& EListIterator<T>::operator--(int)
			{	
				m_Current = m_Current->Prev;
				return (*this);
			}

			template<typename T>
			bool EListIterator<T>::operator==(const EListIterator& itr) const
			{
				return m_Current == itr.m_Current;
			}

			template<typename T>
			bool EListIterator<T>::operator!=(const EListIterator& itr) const
			{
				return m_Current != itr.m_Current;
			}

			template<typename T>
			T& EListIterator<T>::operator*() const
			{
				return *m_Current;
			}

			template<typename T>
			T& EListIterator<T>::operator*()
			{
				return *m_Current;
			}

		} // namespace internal

	} // namespace gtl

} // namespace gecko
